<?php
declare (strict_types = 1);
namespace app\common\middleware;

class CrossDomain
{
	// 完整的HTTP请求头
	private Array $http_header = [];


	// 允许跨域的请求来源
	private Array $allow_origin = [];


	// 允许的HTTP头
	private Array $allow_headers = [
		'Authorization',
		'Content-Type',
		'If-Match',
		'If-Modified-Since',
		'If-None-Match',
		'If-Unmodified-Since',
		'X-CSRF-TOKEN',
		'X-Requested-With'
	];


	// 允许的HTTP方法
	private Array $allow_methods = [
		'GET',
		'POST',
		'PATCH',
		'PUT',
		'DELETE',
		'OPTIONS'
	];


	// 是否允许携带凭据
	private String $allow_credentials = 'true';


	// 预检请求的缓存有效期（秒）
	private Int $max_age = 1800;


	/**
	 * 初始化
	 */
	public function __construct()
	{
		// 加载配置文件
		$cros_conf = config('cros');
		$this->allow_origin = $cros_conf['allow_origin'] ?? [];

		// 若$override_header为非空数组，则使用此数组整体覆盖默认Header
		$override_header = $cros_conf['override_header'] ?? null;
		if( is_array($override_header) && count($override_header) )
		{
			$this->http_header = $override_header;
			return true;
		}

		// 合并“允许的HTTP头”，并去重
		$allow_headers = $cros_conf['allow_headers'] ?? [];
		$allow_headers = array_merge($this->allow_headers, $allow_headers);
		$allow_headers = array_unique($allow_headers);
		// 合并“允许的HTTP方法”，并去重
		$allow_methods = $cros_conf['allow_methods'] ?? [];
		$allow_methods = array_merge($this->allow_methods, $allow_methods);
		$allow_methods = array_unique($allow_methods);
		// 判断是否允许携带凭据（配置文件优先）
		$allow_credentials = $cros_conf['allow_credentials'] ?? $this->allow_credentials;
		$allow_credentials = (string)$allow_credentials;
		// 计算预检请求的缓存有效期（配置文件优先）
		$max_age = $cros_conf['max_age'] ?? $this->max_age;
		$max_age = (int)$max_age;

		// 组装Header
		$this->http_header = [
			'Access-Control-Allow-Headers' => implode(', ', $allow_headers),
			'Access-Control-Allow-Methods' => implode(', ', $allow_methods),
			'Access-Control-Allow-Credentials' => $allow_credentials,
			'Access-Control-Max-Age' => $max_age
		];
	}


	/**
	 * 中间件入口
	 */
	public function handle($request, \Closure $next)
	{
		$response = $next($request);

		// 检查当前请求来源是否允许跨域
		$origin = $request->header('origin');
		if( !in_array($origin, $this->allow_origin) )
		{
			// 不允许跨域
			return $response;
		}

		// 设置跨域头
		$this->http_header['Access-Control-Allow-Origin'] = (string)$origin;
		$response->header($this->http_header);

		// 对于OPTIONS请求返回204 No Content
		if( $request->isOptions() )
		{
			$response->code(204);
		}

		return $response;
	}


}
